/***
 * encodeing: UTF-8
*/

/**
 * 0625 remains:
 * 1 query to schedule
 * no pack
 * more time set
 * partly update
 * whole part test
 * temp test
*/

#include "Room.h"

//int main(void) {
//   DBFacade f;//unique
//   Json::Value powerv, getv, editv, deletev;
//   Json::Value powerv_r, getv_r, editv_r, deletev_r;
//   std::string result;
//   std::vector<Json::Value> to_schedule_list;//in main function, cannot remove it;pop the query out after each call
//   //f.RoomScan(to_schedule_list);//must be called after the birth of object DBFacade
//
//   getv["time"] = 0;
//   getv["type"] = 0;
//   getv["source"] = 0;
//   getv["queryType"] = 5;
//   getv["roomId"] = 0;
//   editv["time"] = 0;
//   editv["type"] = 0;
//   editv["source"] = 0;
//   editv["queryType"] = 6;
//   editv["roomId"] = 0;
//   editv["targetT"] = 28;
//   editv["targetW"] = 1;
//   powerv["time"] = 0;
//   powerv["type"] = 0;
//   powerv["source"] = 0;
//   powerv["queryType"] = 9;
//   powerv["roomId"] = 0;
//   powerv["power"] = 1;
//   deletev["time"] = 0;
//   deletev["type"] = 0;
//   deletev["source"] = 0;
//   deletev["queryType"] = 0;
//   deletev["roomId"] = 0;
//
//   //powerv_r = f.RoomQuery(powerv);
//   //editv_r = f.RoomQuery(editv);
//   //powerv_r = f.RoomQuery(powerv);
//   //result = f.RoomResponse(getv);
//   //std::cout << result << std::endl;
//   //std::string package = pack(getv_r);
//   //Json::Value restract = rest(package);
//   //deletev_r = f.RoomQuery(deletev);
//   /*
//   while (1) {
//       int size = to_schedule_list.size();
//       std::cout << size << std::endl;
//       if (size) {
//           std::cout << to_schedule_list[0] << std::endl;
//       }
//       Sleep(2000);
//   }
//   */
//
//   return 0;
//}

/**
 * dealing with queries from other parts
*/
Json::Value DBFacade::RoomQuery(Json::Value val)
{
   Room room;
   QueryType type = QueryType(val["queryType"].asInt());

   switch (type)
   {
   case QueryType::DETAIL:
   {
      if (room.deleteInfo(val))
      {
         std::cout << "[INFO] RoomBean received a delete-infomation query.\n";
      }
      else
      {
         std::cout << "[ERROR] Failed to delete room infomation in RoomBean.\n";
      }
      break;
   }
   case QueryType::ROOM:
   {
      Json::Value response;

      if (room.getInfo(val, response))
      {
         std::cout << "[INFO] RoomBean received a get-infomation query.\n";
      }
      else
      {
         std::cout << "[ERROR] Failed to get room infomation in RoomBean.\n";
         //exit(-1);
      }
      return response;
   }
   case QueryType::CHANGEROOM:
   {
      if (room.changeInfo(val))
      {
         std::cout << "[INFO] RoomBean received a edit-infomation query.\n";
      }
      else
      {
         std::cout << "[ERROR] Failed to edit room infomation in RoomBean.\n";
      }
      break;
   }
   case QueryType::ONOFF:
   {
      if (room.changePower(val))
      {
         std::cout << "[INFO] RoomBean received a turn-on-off query.\n";
      }
      else
      {
         std::cout << "[ERROR] Failed to edit room infomation in RoomBean.\n";
      }
      break;
   }
   default:
   {
      std::cout << "[ERROR] Wrong query to RoomBean.\n";
      //exit(-1);
   }
   }

   /*Room *del = &room;
    delete(del);*/
   return val;
}

/**
 * updating the temperature each 60 secs
*/
void DBFacade::RoomScanTimer(LPVOID list)
{
   std::vector<Json::Value> *list_p = (std::vector<Json::Value> *)list;

   while (true)
   {
      DBFacade *temp_p = new DBFacade(1);

      temp_p->RoomTemperature(list_p);
      //std::cout << "\n\ntemping\n\n";
      Sleep(TEMPRATURE_UPDATE_TIME);
      //Sleep(5000);
   }
}

/**
 * a sub-thread for updating the temperature
*/
void DBFacade::RoomScan(std::vector<Json::Value> &list)
{
   std::vector<Json::Value> *ptr = &list;

   HANDLE back_scan = CreateThread(
       NULL, 0, LPTHREAD_START_ROUTINE(RoomScanTimer), LPVOID(ptr), 0, NULL);
   CloseHandle(back_scan);
}

/**
 * deal with the result of temperature-editing step
*/
void DBFacade::RoomTemperature(std::vector<Json::Value> *ptr)
{
   std::vector<int> room_list;
   RoomBean bean;
   Room temp;
   Room *find_state = &temp;

   bool judge_list = bean.Query(room_list);
   if (judge_list)
   {
      std::cout << "[INFO] Scanning room id list...\n";
   }
   else
   {
      std::cout << "[ERROR] Failed to read room id list from database.\n";
      exit(-1);
   }

   for (int i = 0; i < room_list.size(); i++)
   {
      find_state->setRoomId(room_list[i]);
      bool judge_find = bean.Query(find_state);

      if (judge_find)
      {
         std::cout << "[INFO] RoomBean reading database...\n";
      }
      else
      {
         std::cout << "[ERROR] Failed to get room infomation from database.\n";
         exit(-1);
      }

      int result = find_state->editTemperature();

      switch (result)
      {
      case 0:
      {
         std::cout << "[ERROR] Failed to edit room " << room_list[i] << " temperature from database.\n";
         break;
      }
      case 1:
      {
         std::cout << "[INFO] Room  " << room_list[i] << " edited.\n";
         break;
      }
      case 2:
      {
         Json::Value response;
         response["time"] = find_state->getTime();
         response["type"] = int(Type::QUERY);
         response["source"] = int(IdentityR::ADMIN);
         response["queryType"] = int(QueryType::WINDSTOP);
         response["roomId"] = find_state->getRoomId();
         ptr->push_back(response);
         break;
      }
      case 3:
      {
         Json::Value response;
         response["time"] = find_state->getTime();
         response["type"] = int(Type::QUERY);
         response["source"] = int(IdentityR::ADMIN);
         response["queryType"] = int(QueryType::WINDSTART);
         response["roomId"] = find_state->getRoomId();
         ptr->push_back(response);
         break;
      }
      default:
      {
         std::cout << "[ERROR] Failed to edit room " << room_list[i] << " temperature from database.\n";
         exit(-1);
      }
      }
   }
}

/**
 * give a response to the caller
*/
Json::Value DBFacade::RoomResponse(Json::Value val)
{
   Json::Value response = this->RoomQuery(val);

   return response;
}

/**
 * inititalize class room
*/
Room::Room(void)
{
   setTime(0);
   setType(Type::QUERY);
   setSource(IdentityR::CLIENT);
   setDestination(IdentityR::CLIENT);
   setQueryType(QueryType::ROOM);
   setResponseType(ResponseType::ROOM);
   setRoomId(0);
   setRoomState(RoomState::WAITING);
   setTargetT(DEFAULT_TEMPRATURE);
   setTargetW(FanSpeed::MEDIUM);
   setCurrentT(DEFAULT_TEMPRATURE);
   setCurrentW(FanSpeed::MEDIUM);
   setInitT(DEFAULT_TEMPRATURE);
   setBill(0);
   setPower(0);
}

/*
std::string Room::pack(Json::Value val) {
    Json::Value response;
    Json::StreamWriterBuilder wbuilder;
    wbuilder.settings_["indentation"] = "\t";
    response["time"] = val["time"];
    response["type"] = val["type"];//
    response["destination"] = val["destination"];//
    response["roomId"] = val["roomId"];//
    response["state"] = val["state"];
    response["target_temp"] = val["target_temp"];
    response["init_temp"] = val["init_temp"];
    response["current_temp"] = val["current_temp"];
    response["fan_speed"] = val["fan_speed"];
    response["nowW"] = val["nowW"];
    response["nowT"] = val["nowT"];
    response["fee"] = val["fee"];
    response["power"] = val["power"];
    std::string document = Json::writeString(wbuilder, response);
    return document;
}
*/

/*
Json::Value rest(std::string body) {
    const char* cbody = body.c_str();
    Json::CharReaderBuilder b;
    Json::CharReader* reader(b.newCharReader());
    Json::Value root;
    JSONCPP_STRING errs;
    bool ok = reader->parse(cbody, cbody + std::strlen(cbody), &root, &errs);

    delete reader;

    return root;
}
*/

/**
 * called when the guest leaves
*/
bool Room::deleteInfo(Json::Value val)
{
   RoomBean bean;

   this->setSource(IdentityR(val["source"].asInt()));
   this->setRoomId(val["roomId"].asInt());

   if (bean.Delete(this))
   {
      std::cout << "[INFO] RoomBean deleting database...\n";
      return true;
   }
   else
   {
      std::cout << "[ERROR] Failed to delete room infomation from database.\n";
      return false;
   }
}

/**
 * called when client/scheduler wants room information
*/
bool Room::getInfo(Json::Value val, Json::Value &response)
{
   RoomBean bean;
   DBFacade *temp = new DBFacade;

   this->setSource(IdentityR(val["source"].asInt()));
   this->setRoomId(val["roomId"].asInt());
   bool judge = bean.Query(this);

   if (judge)
   {
      std::cout << "[INFO] RoomBean reading database...\n";

      response["time"] = this->getTime();
      response["type"] = int(ResponseType::ROOM);       //
      response["destination"] = int(this->getSource()); //
      response["roomId"] = this->getRoomId();           //
      response["state"] = int(this->getRoomState());
      response["target_temp"] = this->getTargetT();
      response["init_temp"] = this->getInitT();
      response["current_temp"] = this->getCurrentT();
      response["fan_speed"] = int(this->getCurrentW());
      response["nowW"] = int(this->getCurrentW());
      response["nowT"] = this->getCurrentT();
      response["fee"] = temp->getRoomBill(int(this->getRoomId()));
      response["power"] = this->getPower();

      return true;
   }
   else
   {
      std::cout << "[ERROR] Failed to get room infomation from database.\n";
      response["time"] = this->getTime();
      response["type"] = int(ResponseType::ROOM);       //
      response["destination"] = int(this->getSource()); //
      response["roomId"] = -1;                          //
      response["state"] = int(this->getRoomState());
      response["target_temp"] = this->getTargetT();
      response["init_temp"] = this->getInitT();
      response["current_temp"] = this->getCurrentT();
      response["fan_speed"] = int(this->getCurrentW());
      response["nowW"] = int(this->getCurrentW());
      response["nowT"] = this->getCurrentT();
      response["fee"] = temp->getRoomBill(int(this->getRoomId()));
      response["power"] = this->getPower();
      return false;
   }
   delete (temp);
}

/**
 * called when room information needs to be edited
*/
bool Room::changeInfo(Json::Value val)
{
   RoomBean bean;

   this->setSource(IdentityR(val["source"].asInt()));
   this->setRoomId(val["roomId"].asInt());
   this->setTargetT(val["targetT"].asInt());
   this->setTargetW(FanSpeed(val["targetW"].asInt()));
   this->setRoomState(RoomState(val["state"].asInt()));

   if (bean.Update(this))
   {
      std::cout << "[INFO] RoomBean updating database...\n";
      return true;
   }
   else
   {
      std::cout << "[ERROR] Failed to get room infomation from database.\n";
      return false;
   }
}

/**
 * called when power change
*/
bool Room::changePower(Json::Value val)
{
   RoomBean bean;
   Room temp;
   Room *find_state = &temp;

   this->setSource(IdentityR(val["source"].asInt()));
   this->setRoomId(val["roomId"].asInt());
   find_state->setRoomId(this->getRoomId());

   bool judge = bean.Query(find_state);
   if (!judge)
   {
      this->setPower(1);
      bean.Insert(this);
      return true;
   }
   else
   {
      if (find_state->getPower() == 0)
      {
         this->setPower(1);
         //this->setRoomState(RoomState::WAITING);
         if (bean.Update_power(this))
         {
            std::cout << "[INFO] RoomBean updating database...\n";
            return true;
         }
         else
         {
            std::cout << "[ERROR] Failed to get room infomation from database.\n";
            return false;
         }
      }
      else if (find_state->getPower() == 1)
      {
         this->setPower(0);
         this->setRoomState(RoomState::POWEROFF);
         if (bean.Update_power(this))
         {
            std::cout << "[INFO] RoomBean updating database...\n";
            return true;
         }
         else
         {
            std::cout << "[ERROR] Failed to get room infomation from database.\n";
            return false;
         }
      }
      else
      {
         std::cout << "[ERROR] Room " << this->getRoomId() << " power status error.\n";
         return false;
      }
   }
}

/**
 * edit temperature
*/
int Room::editTemperature(void)
{
   RoomBean bean;

   bean.Query(this);
   int id = this->getRoomId();
   double ct = this->getCurrentT();
   double tt = this->getTargetT();
   FanSpeed w = this->getCurrentW();
   RoomState s = this->getRoomState();

   if (s != RoomState::SLEEPING)
   {
      if (ct != tt)
      {
         if (s != RoomState::SERVING)
         {
            if (ct < tt)
            {
               this->setCurrentT(ct + MID_WIND_TEMPRATURE);
               //this->setRoomState(RoomState(0));
               //this->setTargetW(FanSpeed(0));
               if (bean.Update_temp(this))
               {
                  std::cout << "[INFO] RoomBean updating database...\n";
                  return 1;
               }
               else
               {
                  std::cout << "[ERROR] Failed to get room infomation from database.\n";
                  return 0;
               }
            }
            else
            {
               this->setCurrentT(ct - MID_WIND_TEMPRATURE);
               //this->setRoomState(RoomState(0));
               //this->setTargetW(FanSpeed(0));
               if (bean.Update_temp(this))
               {
                  std::cout << "[INFO] RoomBean updating database...\n";
                  return 1;
               }
               else
               {
                  std::cout << "[ERROR] Failed to get room infomation from database.\n";
                  return 0;
               }
            }
         }
         else if ((int)s == 0)
         {
            std::cout << "[ERROR] Room state error in room " << id << ".\n";
            return 0;
         }
         else
         {
            double gap = 0;

            switch (w)
            {
            case FanSpeed::HIGH:
            {
               gap = HIGH_WIND_TEMPRATURE;
               break;
            }
            case FanSpeed::MEDIUM:
            {
               gap = MID_WIND_TEMPRATURE;
               break;
            }
            case FanSpeed::LOW:
            {
               gap = LOW_WIND_TEMPRATURE;
               break;
            }
            }
            if (ct < tt)
            {
               this->setCurrentT(ct + gap);
               //this->setRoomState(RoomState(0));
               //this->setTargetW(FanSpeed(0));
               if (bean.Update_temp(this))
               {
                  std::cout << "[INFO] RoomBean updating database...\n";
                  return 1;
               }
               else
               {
                  std::cout << "[ERROR] Failed to get room infomation from database.\n";
                  return 0;
               }
            }
            else
            {
               this->setCurrentT(ct - gap);
               //this->setRoomState(RoomState(0));
               //this->setTargetW(FanSpeed(0));
               if (bean.Update_temp(this))
               {
                  std::cout << "[INFO] RoomBean updating database...\n";
                  return 1;
               }
               else
               {
                  std::cout << "[ERROR] Failed to get room infomation from database.\n";
                  return 0;
               }
            }
         }
      }
      else
      {
         this->setRoomState(RoomState::SLEEPING);
         this->setTargetT(0);
         this->setTargetW(FanSpeed(0));
         this->setPower(0);
         if (bean.Update(this))
         {
            if (bean.Update_power(this))
            {
               std::cout << "[INFO] RoomBean updating database...\n";
               return 2;
            }
            else
            {
               std::cout << "[ERROR] Failed to get room infomation from database.\n";
               return 0;
            }
         }
         else
         {
            std::cout << "[ERROR] Failed to get room infomation from database.\n";
            return 0;
         }
      }
   }
   else
   {
      if (tt < DEFAULT_TEMPRATURE)
      {
         if (ct - tt > 1)
         {
            this->setRoomState(RoomState::WAITING);
            this->setTargetT(0);
            this->setTargetW(FanSpeed(0));
            if (bean.Update(this))
            {
               std::cout << "[INFO] RoomBean updating database...\n";
               return 3;
            }
            else
            {
               std::cout << "[ERROR] Failed to get room infomation from database.\n";
               return 0;
            }
         }
         else
         {
            this->setCurrentT(ct + MID_WIND_TEMPRATURE);
            //this->setRoomState(RoomState(0));
            //this->setTargetW(FanSpeed(0));
            if (bean.Update_temp(this))
            {
               std::cout << "[INFO] RoomBean updating database...\n";
               return 1;
            }
            else
            {
               std::cout << "[ERROR] Failed to get room infomation from database.\n";
               return 0;
            }
         }
      }
      else
      {
         return 1;
      }
   }

   return 0;
}

/**
 * ports
*/
time_t Room::getTime(void)
{
   return _time;
}

Type Room::getType(void)
{
   return _type;
}

IdentityR Room::getSource(void)
{
   return _source;
}

IdentityR Room::getDestination(void)
{
   return _destination;
}

QueryType Room::getQueryType(void)
{
   return _queryType;
}

ResponseType Room::getResponseType(void)
{
   return _responseType;
}

int Room::getRoomId(void)
{
   return _roomId;
}

RoomState Room::getRoomState(void)
{
   return _state;
}

double Room::getTargetT(void)
{
   return _targetT;
}

FanSpeed Room::getTargetW(void)
{
   return _targetW;
}

double Room::getCurrentT(void)
{
   return _nowT;
}

FanSpeed Room::getCurrentW(void)
{
   return _nowW;
}

double Room::getInitT(void)
{
   return _initT;
}

double Room::getBill(void)
{
   return _fee;
}

int Room::getPower(void)
{
   return _power;
}

void Room::setTime(time_t time)
{
   _time = time;
}

void Room::setType(Type type)
{
   _type = type;
}

void Room::setSource(IdentityR src)
{
   _source = src;
}

void Room::setDestination(IdentityR des)
{
   _destination = des;
}

void Room::setQueryType(QueryType query)
{
   _queryType = query;
}

void Room::setResponseType(ResponseType rsp)
{
   _responseType = rsp;
}

void Room::setRoomId(int id)
{
   _roomId = id;
}

void Room::setRoomState(RoomState state)
{
   _state = state;
}

void Room::setTargetT(double t_temp)
{
   _targetT = t_temp;
}

void Room::setTargetW(FanSpeed t_wind)
{
   _targetW = t_wind;
}

void Room::setCurrentT(double c_temp)
{
   _nowT = c_temp;
}

void Room::setCurrentW(FanSpeed c_wind)
{
   _nowW = c_wind;
}

void Room::setInitT(double i_temp)
{
   _initT = i_temp;
}

void Room::setBill(double bill)
{
   _fee = bill;
}

void Room::setPower(int power)
{
   _power = power;
}